# groups current project
# USAGE: GGroupProject(ProjectName GroupPath)
#
# FileName: Name of the project
# GroupPath: Path of the group. Solution folders can be paths such as
#			 "Folder1/Folder2/Folder3" will generate 3 nested folders
function( GGroupProject )

	# Use ARGUMENT_PARSE_TOKENS list to parse incoming arguments.
	parse_arguments( PARSED "" "" ${ARGV} )
	
	# get project name
	list( GET PARSED_DEFAULT_ARGS 0 PARSED_NAME )
	
	# get group name
	list( GET PARSED_DEFAULT_ARGS 1 PARSED_GROUP )
	
	# set projects FOLDER property
	set_property( TARGET ${PARSED_NAME} PROPERTY FOLDER ${PARSED_GROUP} )
	
endfunction()

# adds a project that will not compile
# USAGE: GAddProject( ProjectName SOURCES [Src1 Src2...] )
#
# FileName: Name of the project
# SOURCES: Source files that will be added to the target
function( GAddProject )

	# decide parsing conditions
	set( PARSE_TOKENS "SOURCES" )

	# Use ARGUMENT_PARSE_TOKENS list to parse incoming arguments.
	parse_arguments( PARSED "${PARSE_TOKENS}" "" ${ARGV} )
	
	# get project name
	list( GET PARSED_DEFAULT_ARGS 0 PROJECT_NAME )
	
	# add project
	add_custom_target( ${PROJECT_NAME} SOURCES ${PARSED_SOURCES} )
	
endfunction()

# adds a source file to the specified lists
# USAGE: GAddSource(FileName
#					LISTS [List1 List2...] 
#					PLATFORMS [Plat1 Plat2...] 
#					COMPILERS [Comp1 Comp2...] 
#					ARCHITECS [Arch1 Arch2...] )
#
# FileName: Name of the file
# LISTS: List that file wil lbe added to. Will be created if not available
# PLATFORMS: Platforms that file will be added to the given lists.
# COMPILERS: Compilers that file will be added to the given lists.
# ARCHITECTURES: Architectures that file will be added to the given lists.
function( GAddSource )
	
	# decide parsing conditions
	set( PARSE_TOKENS "LISTS;PLATFORMS;COMPILERS;ARCHITECS" )

	# Use ARGUMENT_PARSE_TOKENS list to parse incoming arguments.
	parse_arguments( PARSED "${PARSE_TOKENS}" "" ${ARGV} )
	
	list_check( PARSED_COMPILERS ${G_HOST_COMPILER} COMP_CHECK )
	list_check( PARSED_PLATFORMS ${G_TARGET_PLATFORM} PLAT_CHECK )
	list_check( PARSED_ARCHITECS ${G_TARGET_ARCHITEC} ARCH_CHECK )
	
	# do not add file to given lists if checks fails
	if ( (NOT PLAT_CHECK) OR (NOT COMP_CHECK) OR (NOT ARCH_CHECK) )
		return()
	endif ()
	
	# get file name
	list( GET PARSED_DEFAULT_ARGS 0 FILE_NAME )
	
	# check if file exists at current dir
	file_exists( ${FILE_NAME} ${CMAKE_CURRENT_SOURCE_DIR} )

	# group all files directly under the project tab
	source_group( "" FILES ${FILE_NAME} )
	
	#add file to specified lists
	foreach (CURRENT_LIST ${PARSED_LISTS})
	
		# append file to the list
		list( APPEND ${CURRENT_LIST} ${FILE_NAME} )
		# update list in parent scope
		set( ${CURRENT_LIST} ${${CURRENT_LIST}} PARENT_SCOPE )
		
	endforeach()
	
endfunction()

# for creating a library with the given lists
# USAGE: GAddLibrary(LibraryName
#					HEADERS [Hdr1 Hdr2...] 
#					SOURCES [Src1 Src2...] 
#					PLATFORMS [Plat1 Plat2...] 
#					COMPILERS [Comp1 Comp2...] 
#					ARCHITECS [Arch1 Arch2...] 
#					SHARED)
#
# LibraryName: Name of the target lib
# HEADERS: List of public header files. These will be copied to output dir
# SOURCES: List of the source files.
# PLATFORMS: Platforms that file will be added to the given lists.
# COMPILERS: Compilers that file will be added to the given lists.
# ARCHITECTURES: Architectures that file will be added to the given lists.
#
# Sets these properties for the target library
# G_TARGET_TYPE: is set to STATIC or SHARED
# G_TARGET_SOURCE_LIST: is set to the source list specified
# G_TARGET_HEADER_LIST: is set to the header list specified
# G_TARGET_BINARY_DIR: is set to the corresponding binary dir
# G_TARGET_SOURCE_DIR: is set to the current dir
function( GAddLibrary )
	
	# decide parsing conditions
	set( PARSE_TOKENS "HEADERS;SOURCES;PLATFORMS;COMPILERS;ARCHITECS" )
	set( OPTIONAL_TOKENS "SHARED" )

	# parse arguments
	parse_arguments( PARSED "${PARSE_TOKENS}" "${OPTIONAL_TOKENS}" ${ARGV} )
	
	list_check( PARSED_COMPILERS ${G_HOST_COMPILER} COMP_CHECK )
	list_check( PARSED_PLATFORMS ${G_TARGET_PLATFORM} PLAT_CHECK )
	list_check( PARSED_ARCHITECS ${G_TARGET_ARCHITEC} ARCH_CHECK )
	
	# do not add library if any check fails
	if ( (NOT PLAT_CHECK) OR (NOT COMP_CHECK) OR (NOT ARCH_CHECK) )
		return()
	endif ()
	
	# get lib name
	list( GET PARSED_DEFAULT_ARGS 0 TARGET_LIB )
	
	# decide shared or static output
	if ( PARSED_SHARED )
		# add library
		add_library( ${TARGET_LIB} SHARED ${PARSED_SOURCES} )
		# set output type for the target in G_OUTPUT_TYPE variable 
		set_property( TARGET ${TARGET_LIB} PROPERTY G_TARGET_TYPE "SHARED" )
	else ()
		# add library
		add_library( ${TARGET_LIB} STATIC ${PARSED_SOURCES} )
		# set output type for the target in G_OUTPUT_TYPE variable
		set_property( TARGET ${TARGET_LIB} PROPERTY G_TARGET_TYPE "STATIC" )
	endif ()
	
	# store extra properties for this target
	set_property( TARGET ${TARGET_LIB} APPEND PROPERTY G_TARGET_SOURCE_LIST "${PARSED_SOURCES}" )
	set_property( TARGET ${TARGET_LIB} APPEND PROPERTY G_TARGET_HEADER_LIST "${PARSED_HEADERS}" )
	set_property( TARGET ${TARGET_LIB} APPEND PROPERTY G_TARGET_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}" )
	set_property( TARGET ${TARGET_LIB} APPEND PROPERTY G_TARGET_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" )
	
	# set the include directory so it wil lbe added to linking library
	target_include_directories( ${TARGET_LIB} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} )
	
	message( "Library ${TARGET_LIB} added to project." )
	
endfunction()

# links the specified library list to previously created target
# USAGE: GLinkLibrary(TargetName
#					LIBS [Lib1 Lib2...] 
#					PLATFORMS [Plat1 Plat2...] 
#					COMPILERS [Comp1 Comp2...] 
#					ARCHITECS [Arch1 Arch2...] )
#
# TargetName: Name of the target lib
# LIBS: List of libs that will be linked to target library
# PLATFORMS: Platforms that file will be added to the given lists.
# COMPILERS: Compilers that file will be added to the given lists.
# ARCHITECTURES: Architectures that file will be added to the given lists.
#
# Sets these properties for the target library
# G_TARGET_DEPENDENCIES: is set to the LIBS list
function( GLinkLibrary )
	
	# decide parsing conditions
	set( PARSE_TOKENS "LIBS;PLATFORMS;COMPILERS;ARCHITECS" )

	# parse arguments
	parse_arguments(PARSED "${PARSE_TOKENS}" "" ${ARGV} )
	
	list_check( PARSED_COMPILERS ${G_HOST_COMPILER} COMP_CHECK )
	list_check( PARSED_PLATFORMS ${G_TARGET_PLATFORM} PLAT_CHECK )
	list_check( PARSED_ARCHITECS ${G_TARGET_ARCHITEC} ARCH_CHECK )
	
	# do not add library if any check fails
	if ( (NOT PLAT_CHECK) OR (NOT COMP_CHECK) OR (NOT ARCH_CHECK) )
		return()
	endif ()
	
	# get lib name
	list( GET PARSED_DEFAULT_ARGS 0 TARGET_LIB )
	
	# add given libraries to the target's list of linked libraries
	set_property( TARGET ${TARGET_LIB} PROPERTY G_TARGET_DEPENDENCIES ${PARAMETER_LIBS} )
	
	# link requested
	target_link_libraries( ${TARGET_LIB} ${PARSED_LIBS} )
	
endfunction()

# adds an executable target
# USAGE: GAddExecutable(TargetName
#					SOURCES [Src1 Src2...] 
#					PLATFORMS [Plat1 Plat2...] 
#					COMPILERS [Comp1 Comp2...] 
#					ARCHITECS [Arch1 Arch2...] )
#
# PLATFORMS: Platforms that file will be added to the given lists.
# COMPILERS: Compilers that file will be added to the given lists.
# ARCHITECTURES: Architectures that file will be added to the given lists.
#
# Sets these properties for the target library
# G_TARGET_TYPE: is set to EXECUTABLE
function( GAddExecutable )

	# decide parsing conditions
	set( PARSE_TOKENS "SOURCES;PLATFORMS;COMPILERS;ARCHITECS" )

	# parse arguments
	parse_arguments(PARSED "${PARSE_TOKENS}" "" ${ARGV})
	
	list_check( PARSED_COMPILERS ${G_HOST_COMPILER} COMP_CHECK )
	list_check( PARSED_PLATFORMS ${G_TARGET_PLATFORM} PLAT_CHECK )
	list_check( PARSED_ARCHITECS ${G_TARGET_ARCHITEC} ARCH_CHECK )
	
	# do not add library if any check fails
	if ( (NOT PLAT_CHECK) OR (NOT COMP_CHECK) OR (NOT ARCH_CHECK) )
		return()
	endif ()
	
	# get target name
	list( GET PARSED_DEFAULT_ARGS 0 TARGET_EXECUTABLE )
	
	# add executable target
	# @NOTE: executable type is fixed to win32 for now
	add_executable( ${TARGET_EXECUTABLE} WIN32 ${PARSED_SOURCES} )
	
	# set target type
	set_property( TARGET ${TARGET_EXECUTABLE} PROPERTY G_TARGET_TYPE "EXECUTABLE" )
	
	message( "Executable ${TARGET_EXECUTABLE} added to project." )
	
endfunction()

# conditionally(by gui) includes the subdir(ModuleName)
# and adds a preprocessor def as G_MODULE_ENABLED_<ModuleName>
# ModuleName must be the name of the subdir
# USAGE: GAddModule(ModuleName
#					PLATFORMS [Plat1 Plat2...] 
#					COMPILERS [Comp1 Comp2...] 
#					ARCHITECS [Arch1 Arch2...] 
#					[DEFAULT])
#
# ModuleName: Name of the module directory. Dir should be a subdir.
# PLATFORMS: Platforms that file will be added to the given lists.
# COMPILERS: Compilers that file will be added to the given lists.
# ARCHITECTURES: Architectures that file will be added to the given lists.
# DEFAULT: Optional parameter that will specify if the module is default(checked and build automatically)
#
# Sets these properties for the GLOBAL scope
# G_ENABLED_MODULES: Module name is added to his list
function( GAddModule )

	# decide parsing conditions
	set( PARSE_TOKENS "PLATFORMS;COMPILERS;ARCHITECS" )
	set( OPTIONAL_TOKENS "DEFAULT" )

	# parse arguments
	parse_arguments( PARSED "${PARSE_TOKENS}" "${OPTIONAL_TOKENS}" ${ARGV} )
	
	list_check( PARSED_COMPILERS ${G_HOST_COMPILER} COMP_CHECK )
	list_check( PARSED_PLATFORMS ${G_TARGET_PLATFORM} PLAT_CHECK )
	list_check( PARSED_ARCHITECS ${G_TARGET_ARCHITEC} ARCH_CHECK )
	
	# do not add library if any check fails
	if ( (NOT PLAT_CHECK) OR (NOT COMP_CHECK) OR (NOT ARCH_CHECK) )
		return()
	endif ()
	
	# get module name
	list( GET PARSED_DEFAULT_ARGS 0 MODULE_NAME )
	string( TOUPPER ${MODULE_NAME} MODULE_NAME_UPPER )
	string( SUBSTRING ${MODULE_NAME_UPPER} 1 -1 MODULE_NAME_UPPER_RAW )
	
	# create cached variable which creates a gui option
	set( BUILD_OPTION_NAME "G_BUILD_MODULE_${MODULE_NAME_UPPER_RAW}" )
	set( ${BUILD_OPTION_NAME} ${PARSED_DEFAULT} CACHE BOOL "" )
	
	# if the build option is checked from gui of defined as deafult
	if ( ${BUILD_OPTION_NAME} )
		
		# enable module by adding its subdir
		add_subdirectory(${MODULE_NAME})
	
		set_property( GLOBAL APPEND PROPERTY G_ENABLED_MODULES ${MODULE_NAME} )
		
		# add module enabled preprocessor definition to source directory.
		# @NOTE:cmake cannot add DIRECTORY property if that directory is not processed yet!!
		set( MODULE_DEFINITION G_MODULE_ENABLED_${MODULE_NAME_UPPER_RAW} )
		set_property( DIRECTORY ${CMAKE_SOURCE_DIR}/Source APPEND PROPERTY COMPILE_DEFINITIONS ${MODULE_DEFINITION} )
			
	endif ()
	
endfunction()



function( GAddExternal )

	message( FATAL_ERROR "Incomplete function GAddExternal()" )

	# decide parsing conditions
	set( PARSE_TOKENS "LIBS;DLLS;PLATFORMS;COMPILERS;ARCHITECS" )
	set( OPTIONAL_TOKENS "STATIC;SHARED;IMPORTED" )

	# parse arguments
	parse_arguments( PARSED "${PARSE_TOKENS}" "${OPTIONAL_TOKENS}" ${ARGV} )

	list_check( PARSED_COMPILERS ${G_HOST_COMPILER} COMP_CHECK )
	list_check( PARSED_PLATFORMS ${G_TARGET_PLATFORM} PLAT_CHECK )
	list_check( PARSED_ARCHITECS ${G_TARGET_ARCHITEC} ARCH_CHECK )
	
	# do not add library if any check fails
	if ( (NOT PLAT_CHECK) OR (NOT COMP_CHECK) OR (NOT ARCH_CHECK) )
		return()
	endif ()
	
	# get module name
	list( GET PARSED_DEFAULT_ARGS 0 TARGET_LIB )
	
	if ( PARSED_STATIC )
		# add library
		add_library( ${TARGET_LIB} STATIC IMPORTED )
		# set import properties
		set_property( TARGET ${TARGET_LIB} PROPERTY IMPORTED_LOCATION c:/path/to/bar.lib ) # LIB directory
	endif ()
	
	if ( PARSED_SHARED )
		# add library	
		add_library( ${TARGET_LIB} SHARED IMPORTED )
		# set import properties
		set_property( TARGET ${TARGET_LIB} PROPERTY IMPORTED_LOCATION c:/path/to/bar.dll ) # DLL directory
	endif ()
	
	if ( PARSED_IMPORTED )
		# add library
		add_library( ${TARGET_LIB} SHARED IMPORTED )
		# set import properties
		set_property( TARGET ${TARGET_LIB} PROPERTY IMPORTED_IMPLIB c:/path/to/bar.lib )	# LIB directory
		set_property( TARGET ${TARGET_LIB} PROPERTY IMPORTED_LOCATION c:/path/to/bar.dll ) # DLL directory
	endif ()
	
	# set the include directory so it will be automatically added to linking library
	target_include_directories( ${TARGET_LIB} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/Headers )
	
	set_property( GLOBAL APPEND PROPERTY G_EXTERNAL_LIB_LIST ${TARGET_LIB} )
	
endfunction()